/**************************************************************************************************
 * red library                                                                                    *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include "red/red.hpp"


namespace red
{
	//=============================================================================================
	// Provides vector operations for the derived class such as addition, multiplication etc.
	// The derived class must provide the following methods:
	//   - real_type element_at(size_t index) const
	//   - real_type& element_at(size_t index)
	//=============================================================================================
	template<typename Derived, typename Real, size_t Dimension>
	class static_vector_base
	{
	public:
		typedef Derived derived_type;
		typedef Real real_type;

        static size_t const dimension = Dimension;

        typedef static_vector_base<derived_type, real_type, dimension> this_type;


		static_vector_base() = default;

		//-----------------------------------------------------------
		// Copy constructor
		//-----------------------------------------------------------
		static_vector_base(static_vector_base const &val)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] = val[i];
		}

		//-----------------------------------------------------------
		// Component access
		//-----------------------------------------------------------
		real_type const& operator [] (size_t index) const
		{
			return derived().element_at(index);
		}

		real_type& operator [] (size_t index)
		{
			return derived().element_at(index);
		}

		//-----------------------------------------------------------
		// Range-based for support
		//-----------------------------------------------------------
		real_type* begin() { return &(*this)[0]; }
		real_type* end() { return begin() + dimension; }

		real_type const* cbegin() const { return &(*this)[0]; }
		real_type const* cend() const { return cbegin() + dimension; }

		//-----------------------------------------------------------
		// Assignment
		//-----------------------------------------------------------
		static_vector_base& operator = (real_type val)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] = val;

			return *this;
		}

		derived_type& operator += (real_type val)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] += val;

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator += (derived_type const &rhs)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] += rhs[i];

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator -= (real_type val)
		{
            for(size_t i = 0; i < derived().dimension(); i++)
				(*this)[i] -= val;

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator -= (static_vector_base const &rhs)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] -= rhs[i];

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator *= (real_type val)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] *= val;

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator *= (static_vector_base const &rhs)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] *= rhs[i];

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator /= (real_type val)
		{
            for(size_t i = 0; i < dimension; i++)
				(*this)[i] /= val;

			return static_cast<derived_type&>(*this);
		}

		derived_type& operator /= (static_vector_base const &rhs)
		{
            for(size_t i = 0; i < dimension; i++)
				(static_cast<derived_type&>(*this))[i] /= rhs[i];

			return static_cast<derived_type&>(*this);
		}

	private:
		derived_type const& derived() const { return static_cast<derived_type const&>(*this); }
		derived_type& derived() { return static_cast<derived_type&>(*this); }
	};

} // namespace: red
